package com.alxbrendmaquinarias.repository;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository("genericDAO")
public class GenericDAOImpl implements GenericDAO {

    protected final Log logger = LogFactory.getLog(getClass());

    @PersistenceContext
    private EntityManager em;

    /**
     * Guarda el Estado y podemos moficar mientras se encuentre en memoria
     *
     * @param <T>
     * @param t
     * @return
     */
    @Transactional
    public <T> T create(T t) {
        em.persist(t);
        return t;
    }

    @Transactional
    public <T> T find(Class<T> type, Object id) {
        return em.find(type, id);
    }

    @Transactional
    public <T> void delete(T t) {
        t = em.merge(t);
        em.remove(t);
    }

    @Transactional
    public <T> T update(T t) {
        return em.merge(t);
    }

    @SuppressWarnings("rawtypes")
    @Transactional
    @Override
    public List findWithNamedQuery(String queryName) {
        return em.createNamedQuery(queryName).getResultList();
    }

    @SuppressWarnings("rawtypes")
    @Override
    public List findWithNamedQuery(String queryName, int resultLimit) {
        return em.createNamedQuery(queryName).setMaxResults(resultLimit)
                .getResultList();
    }

    @SuppressWarnings("rawtypes")
    @Override
    public List findWithNamedQuery(String namedQueryName,
            Map<String, Object> parameters) {
        return findWithNamedQuery(namedQueryName, parameters, 0);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public List findWithNamedQuery(String namedQueryName,
            Map<String, Object> parameters, int resultLimit) {
        Query query = this.em.createNamedQuery(namedQueryName);
        if (resultLimit > 0) {
            query.setMaxResults(resultLimit);
        }
        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.getResultList();
    }

    @Override
    public <T> List<T> findWithNativeQuery(String sql, Class<T> type) {
        return em.createNativeQuery(sql, type).getResultList();
    }

    @Override
    public Object count(String queryName, Map<String, Object> parameters) {
        if (parameters == null) {
            int count = ((Number) em.createNamedQuery(queryName).getSingleResult()).intValue();
            return count;
        } else {
            Query query = this.em.createNamedQuery(queryName);
            for (Map.Entry<String, Object> entry : parameters.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
            int count = ((Number) query.getSingleResult()).intValue();
            return count;

        }
    }
    
    @Override
    public Object countQuery(String query, Map<String, Object> parameters) {
        if (parameters == null) {
            int count = ((Number) em.createQuery(query).getSingleResult()).intValue();
            return count;
        } else {
            Query queryx = this.em.createQuery(query);
            for (Map.Entry<String, Object> entry : parameters.entrySet()) {
                queryx.setParameter(entry.getKey(), entry.getValue());
            }
            int count = ((Number) queryx.getSingleResult()).intValue();
            return count;

        }
    }

    @Override
    public List findWithNamedQueryLazyLoading(String queryName, Map<String, Object> parameters, int start, int finish) {
        if (parameters == null) {
            return em.createNamedQuery(queryName)
                    .setFirstResult(start)
                    .setMaxResults(finish)
                    .getResultList();
        } else {
            Query query = this.em.createNamedQuery(queryName);
            for (Map.Entry<String, Object> entry : parameters.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
            return query.setFirstResult(start)
                    .setMaxResults(finish)
                    .getResultList();
        }
    }

    @Override
    @Transactional
    public void update(String sql, boolean isNative) {
        if (isNative) {
            em.createNativeQuery(sql).executeUpdate();
        } else {
            em.createQuery(sql).executeUpdate();
        }
    }

    @Override
    public Object getByIdLazy(Class clazz, Integer id) {
        return em.find(clazz, id);
    }

    @Override
    public List findWithQuery(String query, Map<String, Object> parameters) {
        return em.createQuery(query).getResultList();
    }

}
